package com.atstudent.gmall.item.biz.impl;


import com.alibaba.fastjson.JSON;
import com.atstudent.gmall.common.cache.anno.GmallCache;
import com.atstudent.gmall.common.cache.service.RedisCacheService;
import com.atstudent.gmall.common.constant.GmallConstant;
import com.atstudent.gmall.common.feign.product.SkuFeignClient;
import com.atstudent.gmall.common.feign.search.SearchFeignClient;
import com.atstudent.gmall.common.result.Result;
import com.atstudent.gmall.item.biz.SkuDetailBizService;
import com.atstudent.gmall.product.entity.SkuInfo;
import com.atstudent.gmall.product.entity.SpuSaleAttr;
import com.atstudent.gmall.product.vo.AttrValueConcatVo;
import com.atstudent.gmall.product.vo.CategoryView;
import com.atstudent.gmall.product.vo.SkuDetailVo;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;


import javax.annotation.PostConstruct;
import javax.sound.midi.Soundbank;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;


/**
 * @Author 薛有朋
 * @CreatDate 2024/12/1 17:50
 **/
@Service
@Slf4j
public class SkuDetailBizServiceImpl implements SkuDetailBizService {

    @Autowired
    private SkuFeignClient skuFeignClient;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    //注入线程池
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    //声明一个布隆过滤器
    //private BloomFilter bloomFilter = BloomFilter.create(Funnels.longFunnel() , 100000 , 0.00001);

    //声明一个本地锁
    private ReentrantLock reentrantLock = new ReentrantLock();

    //注入RedissonClient
    @Autowired
    private RedissonClient redissonClient;

    //注入 获取和存入Redis中数据的方法
    @Autowired
    private RedisCacheService redisCacheService;

    @Autowired
    private SearchFeignClient searchFeignClient;



    //初始化布隆过滤器  分布式布隆过滤器已经在product中初始化成功  此本地布隆过滤器初始化方法不再使用 直接在方法里面通过redissonClient调用
//    @PostConstruct  //此注解表明该方法就是一个初始化方法
//    public void init(){ //服务启动时调用该方法
//        Result<List<Long>> allSkuIds = skuFeignClient.findAllSkuIds();
//        List<Long> skuIds = allSkuIds.getData();
//        //存入到布隆过滤器中
//        skuIds.stream().forEach(skuId -> {
//            bloomFilter.put(skuId);
//        });
//        bloomFilter.put(99L);//存放一个不存在的数据 测试用
//        log.info("布隆过滤器初始化成功了.....");
//
//
//        rBloomFilter = redissonClient.getBloomFilter(GmallConstant.REDIS_SKUID_BLOOM_FILTER);
//
//    }


    //布隆过滤器的方式 使用需添加@Override注解 并把方法名改为item
    public SkuDetailVo itemBloomFilter(Long skuId) {
        RBloomFilter<Object> rBloomFilter = redissonClient.getBloomFilter(GmallConstant.REDIS_SKUID_BLOOM_FILTER);
        //通过布隆过滤器进行判断
        if (!rBloomFilter.contains(skuId)){
            log.info("分布式的布隆过滤器中不存在对应的数据,返回null...");
            return null ;
        }
//        if (!bloomFilter.mightContain(skuId)){
//            log.info("布隆过滤器中不存在对应的数据，返回null");
//            return null;
//        }

        //从Redis中查询数据
        String skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
        if (!StringUtils.isEmpty(skuDetailVoJSON)){
            //判断是否是X
            if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                log.info("从Redis缓存中查询到了X,返回null...");
                return null;
            }else {
                log.info("从Redis缓存中查询到了数据,进行返回...");
                SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                return skuDetailVoRedis;
            }
        }

        //从数据库中查询数据
        SkuDetailVo skuDetailVoFromRpc = findSkuDetailVoFromRpc(skuId);
        if (skuDetailVoFromRpc == null){
            log.info("从数据库中未查询到数据,将X值存入到Redis中...");
            redisTemplate.opsForValue().set(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , GmallConstant.REDIS_NULL_VALUE);
        }else {
            log.info("从数据库中查询到了数据,把真实数据存入到Redis中...");
            //查询出来之后存入到Redis缓存中
            redisTemplate.opsForValue().set(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , JSON.toJSONString(skuDetailVoFromRpc));
        }

        return skuDetailVoFromRpc;
    }

    //查询商品详情数据
    public SkuDetailVo findSkuDetailVoFromRpcThreadPool(Long skuId) {

        //封装查询数据结果
        SkuDetailVo skuDetailVo = new SkuDetailVo();
        CountDownLatch countDownLatch = new CountDownLatch(4);

        //根据skuId查询skuInfo和skuImgList
        //先查询  如果查询不到 则返回null  所以不用使用线程
        Result<SkuInfo> skuInfoAndImgResult = skuFeignClient.findSkuInfoAndImageBySkuId(skuId);
        SkuInfo skuInfoAndImg = skuInfoAndImgResult.getData();
        if (skuInfoAndImg == null){
            log.info("数据库中不存在该数据, skuId: {}" , skuId);
            return null;
        }
        skuDetailVo.setSkuInfo(skuInfoAndImg);

        threadPoolExecutor.submit(() -> {
            log.info("根据skuId查询1,2,3级分类的id和name");
            Result<CategoryView> categoryViewResult = skuFeignClient.findCategoryViewBySkuId(skuId);
            CategoryView categoryView = categoryViewResult.getData();
            skuDetailVo.setCategoryView(categoryView);
            countDownLatch.countDown();
        });

        threadPoolExecutor.submit(() -> {
            log.info("根据skuId查询价格");
            Result<SkuInfo> skuInfoResult = skuFeignClient.findSkuInfoBySkuId(skuId);
            BigDecimal price = skuInfoResult.getData().getPrice();
            skuDetailVo.setPrice(price);
            countDownLatch.countDown();
        });

        threadPoolExecutor.submit(() -> {
            log.info("根据skuId查询销售属性和销售属性值");
            Result<List<SpuSaleAttr>> spuSalAttrBySkuIdResult = skuFeignClient.findSpuSalAttrBySkuId(skuId);
            List<SpuSaleAttr> spuSaleAttrList = spuSalAttrBySkuIdResult.getData();
            skuDetailVo.setSpuSaleAttrList(spuSaleAttrList);
            countDownLatch.countDown();
        });

        threadPoolExecutor.submit(() -> {
            log.info("查询该sku所对应的所有sku的销售属性值的组合");
            Result<List<AttrValueConcatVo>> listResult = skuFeignClient.findSkuAttrValueConcatBySkuId(skuId);
            List<AttrValueConcatVo> concatVoList = listResult.getData();
            Map<String, Long> map = concatVoList
                    .stream()
                    .collect(Collectors.toMap(attrValueConcatVo -> attrValueConcatVo.getAttrValueConcat(), attrValueConcatVo -> attrValueConcatVo.getSkuId()));
            //把map数据转换成json格式
            String toJSONString = JSON.toJSONString(map);
            skuDetailVo.setValuesSkuJson(toJSONString);
            countDownLatch.countDown();
        });
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return skuDetailVo;
    }

    //查询商品详情数据改造 使用
    public SkuDetailVo findSkuDetailVoFromRpc(Long skuId) {
        //封装查询数据结果
        SkuDetailVo skuDetailVo = new SkuDetailVo();

        //根据skuId查询skuInfo和skuImgList
        //先查询  如果查询不到 则返回null  所以不用使用线程
        Result<SkuInfo> skuInfoAndImgResult = skuFeignClient.findSkuInfoAndImageBySkuId(skuId);
        SkuInfo skuInfoAndImg = skuInfoAndImgResult.getData();
        if (skuInfoAndImg == null){
            log.info("数据库中不存在该数据, skuId: {}" , skuId);
            return null;
        }
        skuDetailVo.setSkuInfo(skuInfoAndImg);

        CompletableFuture<Void> cf1 = CompletableFuture.runAsync(() -> {
            log.info("根据skuId查询1,2,3级分类的id和name");
            Result<CategoryView> categoryViewResult = skuFeignClient.findCategoryViewBySkuId(skuId);
            CategoryView categoryView = categoryViewResult.getData();
            skuDetailVo.setCategoryView(categoryView);
        } , threadPoolExecutor);

        CompletableFuture<Void> cf2 = CompletableFuture.runAsync(() -> {
            log.info("根据skuId查询价格");
            Result<SkuInfo> skuInfoResult = skuFeignClient.findSkuInfoBySkuId(skuId);
            BigDecimal price = skuInfoResult.getData().getPrice();
            skuDetailVo.setPrice(price);
        } , threadPoolExecutor);

        CompletableFuture<Void> cf3 = CompletableFuture.runAsync(() -> {
            log.info("根据skuId查询销售属性和销售属性值");
            Result<List<SpuSaleAttr>> spuSalAttrBySkuIdResult = skuFeignClient.findSpuSalAttrBySkuId(skuId);
            List<SpuSaleAttr> spuSaleAttrList = spuSalAttrBySkuIdResult.getData();
            skuDetailVo.setSpuSaleAttrList(spuSaleAttrList);
        } , threadPoolExecutor);

        CompletableFuture<Void> cf4 = CompletableFuture.runAsync(() -> {
            log.info("查询该sku所对应的所有sku的销售属性值的组合");
            Result<List<AttrValueConcatVo>> listResult = skuFeignClient.findSkuAttrValueConcatBySkuId(skuId);
            List<AttrValueConcatVo> concatVoList = listResult.getData();
            Map<String, Long> map = concatVoList
                    .stream()
                    .collect(Collectors.toMap(attrValueConcatVo -> attrValueConcatVo.getAttrValueConcat(), attrValueConcatVo -> attrValueConcatVo.getSkuId()));
            //把map数据转换成json格式
            String toJSONString = JSON.toJSONString(map);
            skuDetailVo.setValuesSkuJson(toJSONString);
        } , threadPoolExecutor);

        CompletableFuture.allOf(cf1 , cf2 , cf3 , cf4).join();

        return skuDetailVo;
    }

    //本地锁的方式
    public SkuDetailVo itemLocalLock(Long skuId) {
        //通过布隆过滤器进行判断
//        if (!bloomFilter.mightContain(skuId)){
//            log.info("布隆过滤器中不存在对应的数据，返回null");
//            return null;
//        }
        //通过分布式布隆过滤器进行判断
        RBloomFilter<Object> rBloomFilter = redissonClient.getBloomFilter(GmallConstant.REDIS_SKUID_BLOOM_FILTER);
        if (!rBloomFilter.contains(skuId)){
            log.info("分布式的布隆过滤器中不存在对应的数据,返回null...");
            return null ;
        }

        //从Redis中查询数据
        String skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
        if (!StringUtils.isEmpty(skuDetailVoJSON)){
            //判断是否是X
            if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                log.info("从Redis缓存中查询到了X,返回null...");
                return null;
            }else {
                log.info("从Redis缓存中查询到了数据,进行返回...");
                SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                return skuDetailVoRedis;
            }
        }

        boolean tryLock = reentrantLock.tryLock();
        if (tryLock){ //拿到了锁
            log.info("线程:{}获取到了锁，从数据库中查询数据..." , Thread.currentThread().getId());
            //从数据库中查询数据
            SkuDetailVo skuDetailVoFromRpc = findSkuDetailVoFromRpc(skuId);
            if (skuDetailVoFromRpc == null){
                log.info("从数据库中未查询到数据,将X值存入到Redis中...");
                redisTemplate.opsForValue().set(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , GmallConstant.REDIS_NULL_VALUE);
            }else {
                log.info("从数据库中查询到了数据,把真实数据存入到Redis中...");
                //查询出来之后存入到Redis缓存中
                redisTemplate.opsForValue().set(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , JSON.toJSONString(skuDetailVoFromRpc));
            }

            //释放锁
            reentrantLock.unlock();

            //返回从数据库中查到的数据
            log.info("从数据库中查到了数据，进行返回...");
            return skuDetailVoFromRpc;
        }else {
            log.info("线程:{}没有获取到锁，从Redis中查询数据..." , Thread.currentThread().getId());
            skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
            if (!StringUtils.isEmpty(skuDetailVoJSON)){
                //判断是否是X
                if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                    log.info("从Redis缓存中查询到了X,返回null...");
                    return null;
                }else {
                    log.info("从Redis缓存中查询到了数据,进行返回...");
                    SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                    return skuDetailVoRedis;
                }
            }else {//Redis中是空的 线程休眠后再次查询
                //线程休眠
                try {
                    TimeUnit.MILLISECONDS.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //再次从Redis中查询数据
                skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
                //判断是否是X
                if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                    log.info("从Redis缓存中查询到了X,返回null...");
                    return null;
                }else {
                    log.info("从Redis缓存中查询到了数据,进行返回...");
                    SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                    return skuDetailVoRedis;
                }
            }
        }
    }

    //分布式锁-加锁方法
    public boolean tryLock(String uuid , Long skuId){
        Boolean absent = redisTemplate.opsForValue().setIfAbsent(GmallConstant.REDIS_ITEM_LOCK_PREFIX + skuId, uuid, 30, TimeUnit.SECONDS);
        return absent;
    }

    //分布式锁-释放锁方法
    public void unLock(String uuid , Long skuId){
        //使用lua脚本保证释放锁的原子性
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end" ;
        //执行lua脚本
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(GmallConstant.REDIS_ITEM_LOCK_PREFIX + skuId), uuid);
        if (result == 1){
            System.out.println("锁释放成功了...");
        }else {
            System.out.println("锁是别人的，释放锁失败...");
        }
    }

    //分布式锁的方式
    public SkuDetailVo itemRedisTemplate(Long skuId) {
        //通过布隆过滤器进行判断
//        if (!bloomFilter.mightContain(skuId)){
//            log.info("布隆过滤器中不存在对应的数据，返回null");
//            return null;
//        }

        //通过分布式布隆过滤器进行判断
        RBloomFilter<Object> rBloomFilter = redissonClient.getBloomFilter(GmallConstant.REDIS_SKUID_BLOOM_FILTER);
        if (!rBloomFilter.contains(skuId)){
            log.info("分布式的布隆过滤器中不存在对应的数据,返回null...");
            return null ;
        }

        //从Redis中查询数据
        String skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
        if (!StringUtils.isEmpty(skuDetailVoJSON)){
            //判断是否是X
            if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                log.info("从Redis缓存中查询到了X,返回null...");
                return null;
            }else {
                log.info("从Redis缓存中查询到了数据,进行返回...");
                SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                return skuDetailVoRedis;
            }
        }

        String uuid = UUID.randomUUID().toString().replace("-", "");
        boolean tryLock = tryLock(uuid, skuId);
        if (tryLock){ //拿到了锁
            log.info("线程:{}获取到了锁，从数据库中查询数据..." , Thread.currentThread().getId());
            SkuDetailVo skuDetailVoFromRpc = null;
            try {
                //从数据库中查询数据
                skuDetailVoFromRpc = findSkuDetailVoFromRpc(skuId);
                if (skuDetailVoFromRpc == null){
                    log.info("从数据库中未查询到数据,将X值存入到Redis中...");
                    redisTemplate.opsForValue().set(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , GmallConstant.REDIS_NULL_VALUE);
                }else {
                    log.info("从数据库中查询到了数据,把真实数据存入到Redis中...");
                    //查询出来之后存入到Redis缓存中
                    redisTemplate.opsForValue().set(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , JSON.toJSONString(skuDetailVoFromRpc));
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //释放锁
                unLock(uuid , skuId);
            }

            //返回从数据库中查到的数据
            log.info("从数据库中查到了数据，进行返回...");
            return skuDetailVoFromRpc;
        }else {
            log.info("线程:{}没有获取到锁，从Redis中查询数据..." , Thread.currentThread().getId());
            skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
            if (!StringUtils.isEmpty(skuDetailVoJSON)){
                //判断是否是X
                if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                    log.info("从Redis缓存中查询到了X,返回null...");
                    return null;
                }else {
                    log.info("从Redis缓存中查询到了数据,进行返回...");
                    SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                    return skuDetailVoRedis;
                }
            }else {//Redis中是空的 线程休眠后再次查询
                //线程休眠
                try {
                    TimeUnit.MILLISECONDS.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //再次从Redis中查询数据
                skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
                //判断是否是X
                if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                    log.info("从Redis缓存中查询到了X,返回null...");
                    return null;
                }else {
                    log.info("从Redis缓存中查询到了数据,进行返回...");
                    SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                    return skuDetailVoRedis;
                }
            }
        }
    }

    //Redisson框架实现分布式锁
    public SkuDetailVo itemRedisson(Long skuId) {
        //通过布隆过滤器进行判断
//        if (!bloomFilter.mightContain(skuId)){
//            log.info("布隆过滤器中不存在对应的数据，返回null");
//            return null;
//        }

        //通过分布式布隆过滤器进行判断
        RBloomFilter<Object> rBloomFilter = redissonClient.getBloomFilter(GmallConstant.REDIS_SKUID_BLOOM_FILTER);
        if (!rBloomFilter.contains(skuId)){
            log.info("分布式的布隆过滤器中不存在对应的数据,返回null...");
            return null ;
        }

        //从Redis中查询数据
        String skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
        if (!StringUtils.isEmpty(skuDetailVoJSON)){
            //判断是否是X
            if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                log.info("从Redis缓存中查询到了X,返回null...");
                return null;
            }else {
                log.info("从Redis缓存中查询到了数据,进行返回...");
                SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                return skuDetailVoRedis;
            }
        }

        RLock rLock = redissonClient.getLock(GmallConstant.REDIS_ITEM_LOCK_PREFIX + skuId);
        boolean tryLock = rLock.tryLock();
        if (tryLock){ //拿到了锁
            log.info("线程:{}获取到了锁，从数据库中查询数据..." , Thread.currentThread().getId());
            SkuDetailVo skuDetailVoFromRpc = null;
            try {
                //从数据库中查询数据
                skuDetailVoFromRpc = findSkuDetailVoFromRpc(skuId);
                if (skuDetailVoFromRpc == null){
                    log.info("从数据库中未查询到数据,将X值存入到Redis中...");
                    redisTemplate.opsForValue().set(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , GmallConstant.REDIS_NULL_VALUE);
                }else {
                    log.info("从数据库中查询到了数据,把真实数据存入到Redis中...");
                    //查询出来之后存入到Redis缓存中
                    redisTemplate.opsForValue().set(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , JSON.toJSONString(skuDetailVoFromRpc));
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //释放锁
                rLock.unlock();
            }

            //返回从数据库中查到的数据
            log.info("从数据库中查到了数据，进行返回...");
            return skuDetailVoFromRpc;
        }else {
            log.info("线程:{}没有获取到锁，从Redis中查询数据..." , Thread.currentThread().getId());
            skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
            if (!StringUtils.isEmpty(skuDetailVoJSON)){
                //判断是否是X
                if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                    log.info("从Redis缓存中查询到了X,返回null...");
                    return null;
                }else {
                    log.info("从Redis缓存中查询到了数据,进行返回...");
                    SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                    return skuDetailVoRedis;
                }
            }else {//Redis中是空的 线程休眠后再次查询
                //线程休眠
                try {
                    TimeUnit.MILLISECONDS.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //再次从Redis中查询数据
                skuDetailVoJSON = redisTemplate.opsForValue().get(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId);
                //判断是否是X
                if (GmallConstant.REDIS_NULL_VALUE.equalsIgnoreCase(skuDetailVoJSON)){
                    log.info("从Redis缓存中查询到了X,返回null...");
                    return null;
                }else {
                    log.info("从Redis缓存中查询到了数据,进行返回...");
                    SkuDetailVo skuDetailVoRedis = JSON.parseObject(skuDetailVoJSON, SkuDetailVo.class);
                    return skuDetailVoRedis;
                }
            }
        }
    }

    //Redisson框架实现分布式锁-提取公共代码后的精简版本
    public SkuDetailVo itemRedissonClient(Long skuId) {
        //通过布隆过滤器进行判断
//        if (!bloomFilter.mightContain(skuId)){
//            log.info("布隆过滤器中不存在对应的数据，返回null");
//            return null;
//        }

        //通过分布式布隆过滤器进行判断
        RBloomFilter<Object> rBloomFilter = redissonClient.getBloomFilter(GmallConstant.REDIS_SKUID_BLOOM_FILTER);
        if (!rBloomFilter.contains(skuId)){
            log.info("分布式的布隆过滤器中不存在对应的数据,返回null...");
            return null ;
        }

        //从Redis中查询数据 如果为X已经在getData方法中处理（抛出异常），如果有真实数据，则返回。
        SkuDetailVo skuDetailVo = redisCacheService.getData(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId, SkuDetailVo.class);
        if (skuDetailVo != null){
            log.info("从Redis缓存中查询到了数据,进行返回...");
            return skuDetailVo;
        }

        //如果Redis中查到的是null 则加锁去数据库中查询数据
        RLock rLock = redissonClient.getLock(GmallConstant.REDIS_ITEM_LOCK_PREFIX + skuId);
        boolean tryLock = rLock.tryLock();
        if (tryLock){ //拿到了锁
            log.info("线程:{}获取到了锁，从数据库中查询数据..." , Thread.currentThread().getId());
            SkuDetailVo skuDetailVoFromRpc = null;
            try {
                //从数据库中查询数据,查询出来之后存入到Redis缓存中
                skuDetailVoFromRpc = findSkuDetailVoFromRpc(skuId);
                if (skuDetailVoFromRpc == null){
                    log.info("从数据库中未查询到数据,将X值存入到Redis中...");
                    redisCacheService.setData(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , GmallConstant.REDIS_NULL_VALUE);
                }else {
                    log.info("从数据库中查询到了数据,把真实数据存入到Redis中...");
                    redisCacheService.setData(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , skuDetailVoFromRpc);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //释放锁
                rLock.unlock();
            }

            //返回从数据库中查到的数据
            log.info("从数据库中查到了数据，进行返回...");
            return skuDetailVoFromRpc;
        }else {
            log.info("线程:{}没有获取到锁，从Redis中查询数据..." , Thread.currentThread().getId());
            SkuDetailVo detailVo = redisCacheService.getData(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId, SkuDetailVo.class);
            if (detailVo != null){
                log.info("从Redis缓存中查询到了数据,进行返回...");
                return detailVo;
            }

            //Redis中是空的 线程休眠后再次查询
            //线程休眠
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            //再次从Redis中查询数据
            return redisCacheService.getData(GmallConstant.REDSI_SKU_DETAIL_PREFIX + skuId , SkuDetailVo.class);

        }
    }

    //最终版本
    @Override
    @GmallCache(
            cacheKey = GmallConstant.REDSI_SKU_DETAIL_PREFIX + "#{#params[0]}" ,
            bloomFilterName = GmallConstant.REDIS_SKUID_BLOOM_FILTER ,
            bloomFilterValue = "#{#params[0]}" ,
            enableLock = true ,
            lockName = GmallConstant.REDIS_ITEM_LOCK_PREFIX + "#{#params[0]}" ,
            time = 7 ,
            timeUnit = TimeUnit.DAYS
    )
    public SkuDetailVo item(Long skuId) {
        SkuDetailVo skuDetailVo = skuFeignClient.findSkuDetailVo(skuId).getData();
        return skuDetailVo;
    }

    @Override
    public void updateHotScore(Long skuId) {
        Long hotScore = redisTemplate.opsForValue().increment(GmallConstant.REDIS_SKU_HOT_SCORE_PRE + skuId);
        if (hotScore % 5 == 0){ //为了方便测试每点击5次就更新一次es
            //远程调用热度+1的接口
            searchFeignClient.updateHotScore(skuId , hotScore);
        }
    }
}
